#include "list.h"

typedef struct ListElmt_tag{
	void*		data;
	ListElmt*	next;
}ListElmt;

typedef struct List_tag{
	int			size;

	int (*match)(const void* key1, const void* key2);
	void (*destroy)(void* data);

	ListElmt*	head;
	ListElmt*	tail;
}List;


List* list_create(void(*destroy)(void* data))
{
	List* list;
	assert(NULL != (list=(List* )malloc(sizeof(List))));

	list->size = 0;
	list->destroy = destroy;

	list->head = NULL;
	list->tail = NULL;

	return list;
}
void list_destroy(List* list)
{
	void* data;
	assert(NULL != list);
	assert(NULL != list->destroy);

	while(list->size > 0)
	{
		if (list_rmv_next(list, NULL, &data)==0)
		{
			list->destroy(data);
		}
	}
	free(list);
	list = NULL;
}
int list_ins_next(List* list, \
				  ListElmt* element, const void* data)
{	
	ListElmt* new_element;
	assert(NULL != list);
	if(NULL == (new_element=(ListElmt* )malloc(sizeof(ListElmt))))
		return -1;

	new_element->data = (void *)data;
	if (NULL == element)			//insert as head
	{
		new_element->next = list->head;
		list->head = new_element;
		if (list->size==0)
			list->tail = new_element;
	}
	else							//insert after head
	{
		new_element->next = element->next;
		element->next = new_element;
		if (element == list_tail(list))
		{
			list->tail = new_element;
		}
	}

	list->size++;
	return 0;
}
int list_rmv_next(List* list, \
				  ListElmt* element, void** data)
{
	ListElmt* old_element;
	assert(NULL != list && list->size > 0);
	if (NULL == element)			//remove head
	{
		old_element = list->head;
		*data = old_element->data;
		if (list->size == 1)
		{
			list->head = NULL;
			list->tail = NULL;
		}
		else
		{
			list->head = list->head->next;
		}
	} 
	else
	{
		if (list_is_tail(element))		//can't remove the element after tail
		{
			return -1;
		}
		else
		{
			old_element = element->next;
			*data = old_element->data;
			element->next = old_element->next;
			if (old_element->next==NULL)		//if remove tail
			{
				list->tail = element;
			}
		}
	}
	
	free(old_element);
	list->size--;
	return 0;
}
int list_size(const List* list)
{
	return list->size;
}
ListElmt* list_head(const List* list)
{
	return list->head;
}
ListElmt* list_tail(const List* list)
{
	return list->tail;
}
int list_is_head(const List* list, \
				 const ListElmt* element)
{
	return (list->head == element ? 1 : 0);
}
int list_is_tail(const ListElmt* element)
{
	return (element->next == NULL ? 1 : 0);
}
ListElmt* list_next(const ListElmt* element)
{
	return element->next;
}
void* list_data(const ListElmt* element)
{
	return element->data;
}

void list_set_match(List* list, \
					int (*match)(const void* key1, const void* key2))
{
	list->match = match;
}

int list_match(const List* list, \
			   const void* key1, const void* key2)
{
	return list->match(key1, key2);
}

int list_space(void)
{
	return sizeof(List);
}

void list_debug(const List* list)
{
	ListElmt* element;
	if (list->size > 0)
	{
		for (element = list_head(list);element != NULL ; element = list_next(element))
		{
			printf("%d->",*(int*)list_data(element));
		}
	}
}